{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Dispersion: A Full Two Fluid Solution\n",
    "\n",
    "[tfds]: ../../api/plasmapy.dispersion.analytical.two_fluid_.two_fluid.rst\n",
    "[bellan2012]: https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2012JA017856\n",
    "[stringer1963]: https://doi.org/10.1088/0368-3281/5/2/304\n",
    "\n",
    "This notebook walks through the functionality of the [two_fluid()][tfds] function.  This function computes the wave frequencies for given wavenumbers and plasma parameters based on the analytical solution presented by [Bellan 2012][bellan2012] to the [Stringer 1963][stringer1963] two fluid dispersion relation.  The two fluid dispersion equaiton assumes a uniform magnetic field, a zero D.C. electric field, and low-frequency waves $\\omega / k c \\ll 1$ which equates to\n",
    "\n",
    "$$\n",
    "    \\left( \\cos^2 \\theta - Q \\frac{\\omega^2}{k^2 {v_A}^2} \\right)\n",
    "    \\left[\n",
    "        \\left( \\cos^2 \\theta - \\frac{\\omega^2}{k^2 {c_s}^2} \\right)\n",
    "        - Q \\frac{\\omega^2}{k^2 {v_A}^2} \\left(\n",
    "            1 - \\frac{\\omega^2}{k^2 {c_s}^2}\n",
    "        \\right)\n",
    "    \\right] \\\\\n",
    "    = \\left(1 - \\frac{\\omega^2}{k^2 {c_s}^2} \\right)\n",
    "      \\frac{\\omega^2}{{\\omega_{ci}}^2} \\cos^2 \\theta\n",
    "$$\n",
    "\n",
    "where\n",
    "\n",
    "$$Q = 1 + k^2 c^2/{\\omega_{pe}}^2$$\n",
    "\n",
    "$$\\cos \\theta = \\frac{k_z}{k}$$\n",
    "\n",
    "$$\\mathbf{B_o} = B_{o} \\mathbf{\\hat{z}}$$\n",
    "\n",
    "$\\omega$ is the wave frequency, $k$ is the wavenumber, $v_A$ is the Alfvén velocity, $c_s$ is the sound speed, $\\omega_{ci}$ is the ion gyrofrequency, and $\\omega_{pe}$ is the electron plasma frequency.\n",
    "\n",
    "The approach outlined in Section 5 of [Bellan 2012][bellan2012] produces exact roots to the above dispersion equation for all three modes (fast, acoustic, and Alfvén) without having to make additional approximations.  The following dispersion relation is what the [two_fluid()][tfds] function computes.\n",
    "\n",
    "$$\n",
    "    \\frac{\\omega}{\\omega_{ci}} = \\sqrt{\n",
    "        2 \\Lambda \\sqrt{-\\frac{P}{3}} \\cos\\left(\n",
    "            \\frac{1}{3} \\cos^{-1}\\left(\n",
    "                \\frac{3q}{2p} \\sqrt{-\\frac{3}{p}}\n",
    "            \\right)\n",
    "            - \\frac{2 \\pi}{3}j\n",
    "        \\right)\n",
    "        + \\frac{\\Lambda A}{3}\n",
    "    }\n",
    "$$\n",
    "\n",
    "where $j = 0$ represents the fast mode, $j = 1$ represents the Alfvén mode, and $j = 2$ represents the acoustic mode.  Additionally,\n",
    "\n",
    "$$p = \\frac{3B-A^2}{3} \\; , \\; q = \\frac{9AB-2A^3-27C}{27}$$\n",
    "\n",
    "$$A = \\frac{Q + Q^2 \\beta + Q \\alpha + \\alpha \\Lambda}{Q^2} \\;\n",
    "    , \\; B = \\alpha \\frac{1 + 2 Q \\beta + \\Lambda \\beta}{Q^2} \\;\n",
    "    , \\; C = \\frac{\\alpha^2 \\beta}{Q^2}$$\n",
    "\n",
    "$$\\alpha = \\cos^2 \\theta \\;\n",
    "    , \\; \\beta = \\left( \\frac{c_s}{v_A}\\right)^2 \\;\n",
    "    , \\; \\Lambda = \\left( \\frac{k v_{A}}{\\omega_{ci}}\\right)^2$$\n",
    "\n",
    "## Contents:\n",
    "\n",
    "1. [Wave Propagating at 45 Degrees](#Wave-Propagating-at-45-Degrees)\n",
    "2. [Wave frequencies on the k-theta plane](#Wave-frequencies-on-the-k-theta-plane)\n",
    "3. [Reproduce Figure 1 from Bellan 2012](#Reproduce-Figure-1-from-Bellan-2012)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import astropy.units as u\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "from astropy.constants.si import c\n",
    "from matplotlib.ticker import MultipleLocator\n",
    "from mpl_toolkits.axes_grid1 import make_axes_locatable\n",
    "\n",
    "from plasmapy.dispersion.analytical.two_fluid_ import two_fluid\n",
    "from plasmapy.formulary import speeds\n",
    "from plasmapy.formulary.frequencies import gyrofrequency, plasma_frequency, wc_, wp_\n",
    "from plasmapy.formulary.lengths import inertial_length\n",
    "from plasmapy.particles import Particle\n",
    "\n",
    "plt.rcParams[\"figure.figsize\"] = [10.5, 0.56 * 10.5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Wave Propagating at 45 Degrees\n",
    "\n",
    "Below we define the required parameters to compute the wave frequencies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define input parameters\n",
    "inputs = {\n",
    "    \"k\": np.linspace(10**-7, 10**-2, 10000) * u.rad / u.m,\n",
    "    \"theta\": 45 * u.deg,\n",
    "    \"n_i\": 5 * u.cm**-3,\n",
    "    \"B\": 8.3e-9 * u.T,\n",
    "    \"T_e\": 1.6e6 * u.K,\n",
    "    \"T_i\": 4.0e5 * u.K,\n",
    "    \"ion\": Particle(\"p+\"),\n",
    "}\n",
    "\n",
    "# a few useful plasma parameters\n",
    "params = {\n",
    "    \"n_e\": inputs[\"n_i\"] * abs(inputs[\"ion\"].charge_number),\n",
    "    \"cs\": speeds.ion_sound_speed(\n",
    "        inputs[\"T_e\"],\n",
    "        inputs[\"T_i\"],\n",
    "        inputs[\"ion\"],\n",
    "    ),\n",
    "    \"va\": speeds.Alfven_speed(\n",
    "        inputs[\"B\"],\n",
    "        inputs[\"n_i\"],\n",
    "        ion=inputs[\"ion\"],\n",
    "    ),\n",
    "    \"wci\": gyrofrequency(inputs[\"B\"], inputs[\"ion\"]),\n",
    "}\n",
    "params[\"lpe\"] = inertial_length(params[\"n_e\"], \"e-\")\n",
    "params[\"wpe\"] = plasma_frequency(params[\"n_e\"], \"e-\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The computed wave frequencies ($rad/s$) are returned in a dictionary with keys representing the wave modes and the values being an Astropy [Quantity](https://docs.astropy.org/en/stable/api/astropy.units.Quantity.html#astropy.units.Quantity).  Since our inputs had a scalar $\\theta$ and a 1D array of $k$'s, the computed wave frequencies will be a 1D array of size equal to the size of the $k$ array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# compute\n",
    "omegas = two_fluid(**inputs)\n",
    "\n",
    "(list(omegas.keys()), omegas[\"fast_mode\"], omegas[\"fast_mode\"].shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's plot the results of each wave mode."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fs = 14  # default font size\n",
    "figwidth, figheight = plt.rcParams[\"figure.figsize\"]\n",
    "figheight = 1.6 * figheight\n",
    "fig = plt.figure(figsize=[figwidth, figheight])\n",
    "\n",
    "# normalize data\n",
    "k_prime = inputs[\"k\"] * params[\"lpe\"]\n",
    "\n",
    "# plot\n",
    "plt.plot(\n",
    "    k_prime,\n",
    "    np.real(omegas[\"fast_mode\"] / params[\"wpe\"]),\n",
    "    \"r.\",\n",
    "    ms=1,\n",
    "    label=\"Fast\",\n",
    ")\n",
    "ax = plt.gca()\n",
    "ax.plot(\n",
    "    k_prime,\n",
    "    np.real(omegas[\"alfven_mode\"] / params[\"wpe\"]),\n",
    "    \"b.\",\n",
    "    ms=1,\n",
    "    label=\"Alfvén\",\n",
    ")\n",
    "ax.plot(\n",
    "    k_prime,\n",
    "    np.real(omegas[\"acoustic_mode\"] / params[\"wpe\"]),\n",
    "    \"g.\",\n",
    "    ms=1,\n",
    "    label=\"Acoustic\",\n",
    ")\n",
    "\n",
    "# adjust axes\n",
    "ax.set_xlabel(r\"$kc / \\omega_{pe}$\", fontsize=fs)\n",
    "ax.set_ylabel(r\"$Re(\\omega / \\omega_{pe})$\", fontsize=fs)\n",
    "ax.set_yscale(\"log\")\n",
    "ax.set_xscale(\"log\")\n",
    "ax.set_ylim(1e-6, 2e-2)\n",
    "ax.tick_params(\n",
    "    which=\"both\",\n",
    "    direction=\"in\",\n",
    "    width=1,\n",
    "    labelsize=fs,\n",
    "    right=True,\n",
    "    length=5,\n",
    ")\n",
    "\n",
    "# annotate\n",
    "text = (\n",
    "    rf\"$v_A/c_s = {params['va'] / params['cs']:.1f} \\qquad \"\n",
    "    rf\"c/v_A = 10^{np.log10(c / params['va']):.0f} \\qquad \"\n",
    "    f\"\\\\theta = {inputs['theta'].value:.0f}\"\n",
    "    \"^{\\\\circ}$\"\n",
    ")\n",
    "ax.text(0.25, 0.95, text, transform=ax.transAxes, fontsize=18)\n",
    "ax.legend(loc=\"upper left\", markerscale=5, fontsize=fs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Wave frequencies on the k-theta plane\n",
    "\n",
    "Let us now look at the distribution of $\\omega$ on a $k$-$\\theta$ plane."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define input parameters\n",
    "inputs = {\n",
    "    \"k\": np.linspace(10**-7, 10**-2, 10000) * u.rad / u.m,\n",
    "    \"theta\": np.linspace(5, 85, 100) * u.deg,\n",
    "    \"n_i\": 5 * u.cm**-3,\n",
    "    \"B\": 8.3e-9 * u.T,\n",
    "    \"T_e\": 1.6e6 * u.K,\n",
    "    \"T_i\": 4.0e5 * u.K,\n",
    "    \"ion\": Particle(\"p+\"),\n",
    "}\n",
    "\n",
    "# a few useful plasma parameters\n",
    "params = {\n",
    "    \"n_e\": inputs[\"n_i\"] * abs(inputs[\"ion\"].charge_number),\n",
    "    \"cs\": speeds.ion_sound_speed(\n",
    "        inputs[\"T_e\"],\n",
    "        inputs[\"T_i\"],\n",
    "        inputs[\"ion\"],\n",
    "    ),\n",
    "    \"va\": speeds.Alfven_speed(\n",
    "        inputs[\"B\"],\n",
    "        inputs[\"n_i\"],\n",
    "        ion=inputs[\"ion\"],\n",
    "    ),\n",
    "    \"wci\": gyrofrequency(inputs[\"B\"], inputs[\"ion\"]),\n",
    "}\n",
    "params[\"lpe\"] = inertial_length(params[\"n_e\"], \"e-\")\n",
    "params[\"wpe\"] = plasma_frequency(params[\"n_e\"], \"e-\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since the $\\theta$ and $k$ values are now 1-D arrays, the returned wave frequencies will be 2-D arrays with the first dimension matching the size of $k$ and the second dimension matching the size of $\\theta$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# compute\n",
    "omegas = two_fluid(**inputs)\n",
    "\n",
    "(\n",
    "    omegas[\"fast_mode\"].shape,\n",
    "    omegas[\"fast_mode\"].shape[0] == inputs[\"k\"].size,\n",
    "    omegas[\"fast_mode\"].shape[1] == inputs[\"theta\"].size,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's plot (the fast mode)!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fs = 14  # default font size\n",
    "figwidth, figheight = plt.rcParams[\"figure.figsize\"]\n",
    "figheight = 1.6 * figheight\n",
    "fig = plt.figure(figsize=[figwidth, figheight])\n",
    "\n",
    "# normalize data\n",
    "k_prime = inputs[\"k\"] * params[\"lpe\"]\n",
    "zdata = np.transpose(np.real(omegas[\"fast_mode\"].value)) / params[\"wpe\"].value\n",
    "\n",
    "# plot\n",
    "im = plt.imshow(\n",
    "    zdata,\n",
    "    aspect=\"auto\",\n",
    "    origin=\"lower\",\n",
    "    extent=[\n",
    "        np.min(k_prime.value),\n",
    "        np.max(k_prime.value),\n",
    "        np.min(inputs[\"theta\"].value),\n",
    "        np.max(inputs[\"theta\"].value),\n",
    "    ],\n",
    "    interpolation=None,\n",
    "    cmap=plt.cm.Spectral,\n",
    ")\n",
    "ax = plt.gca()\n",
    "\n",
    "# # adjust axes\n",
    "ax.set_xscale(\"linear\")\n",
    "ax.set_xlabel(r\"$kc/\\omega_{pe}$\", fontsize=fs)\n",
    "ax.set_ylabel(r\"$\\theta$ [$deg.$]\", fontsize=fs)\n",
    "ax.tick_params(\n",
    "    which=\"both\",\n",
    "    direction=\"in\",\n",
    "    width=2,\n",
    "    labelsize=fs,\n",
    "    right=True,\n",
    "    top=True,\n",
    "    length=10,\n",
    ")\n",
    "\n",
    "# Add colorbar\n",
    "divider = make_axes_locatable(ax)\n",
    "cax = divider.append_axes(\"top\", size=\"5%\", pad=0.07)\n",
    "cbar = plt.colorbar(\n",
    "    im,\n",
    "    cax=cax,\n",
    "    orientation=\"horizontal\",\n",
    "    ticks=None,\n",
    "    fraction=0.05,\n",
    "    pad=0.0,\n",
    ")\n",
    "cbar.ax.tick_params(\n",
    "    axis=\"x\",\n",
    "    direction=\"in\",\n",
    "    width=2,\n",
    "    length=10,\n",
    "    top=True,\n",
    "    bottom=False,\n",
    "    labelsize=fs,\n",
    "    pad=0.0,\n",
    "    labeltop=True,\n",
    "    labelbottom=False,\n",
    ")\n",
    "cbar.ax.xaxis.set_label_position(\"top\")\n",
    "cbar.set_label(r\"$\\omega/\\omega_{pe}$\", fontsize=fs, labelpad=8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Reproduce Figure 1 from Bellan 2012\n",
    "\n",
    "[bellan2012]: https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2012JA017856\n",
    "\n",
    "\n",
    "Figure 1 of [Bellan 2012][bellan2012] chooses parameters such that $\\beta = 0.4$ and $\\Lambda=0.4$.  Below we define parameters to approximate Bellan's assumptions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define input parameters\n",
    "inputs = {\n",
    "    \"B\": 400e-4 * u.T,\n",
    "    \"ion\": Particle(\"He+\"),\n",
    "    \"n_i\": 6.358e19 * u.m**-3,\n",
    "    \"T_e\": 20 * u.eV,\n",
    "    \"T_i\": 10 * u.eV,\n",
    "    \"theta\": np.linspace(0, 90) * u.deg,\n",
    "    \"k\": (2 * np.pi * u.rad) / (0.56547 * u.m),\n",
    "}\n",
    "\n",
    "# a few useful plasma parameters\n",
    "params = {\n",
    "    \"n_e\": inputs[\"n_i\"] * abs(inputs[\"ion\"].charge_number),\n",
    "    \"cs\": speeds.cs_(inputs[\"T_e\"], inputs[\"T_i\"], inputs[\"ion\"]),\n",
    "    \"wci\": wc_(inputs[\"B\"], inputs[\"ion\"]),\n",
    "    \"va\": speeds.va_(inputs[\"B\"], inputs[\"n_i\"], ion=inputs[\"ion\"]),\n",
    "}\n",
    "params[\"beta\"] = (params[\"cs\"] / params[\"va\"]).value ** 2\n",
    "params[\"wpe\"] = wp_(params[\"n_e\"], \"e-\")\n",
    "params[\"Lambda\"] = (inputs[\"k\"] * params[\"va\"] / params[\"wci\"]).value ** 2\n",
    "\n",
    "(params[\"beta\"], params[\"Lambda\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# compute\n",
    "omegas = two_fluid(**inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# generate data for plots\n",
    "plt_vals = {}\n",
    "for mode, arr in omegas.items():\n",
    "    norm = (np.absolute(arr) / (inputs[\"k\"] * params[\"va\"])).value ** 2\n",
    "    plt_vals[mode] = {\n",
    "        \"x\": norm * np.sin(inputs[\"theta\"].to(u.rad).value),\n",
    "        \"y\": norm * np.cos(inputs[\"theta\"].to(u.rad).value),\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "nbsphinx-thumbnail": {
     "tooltip": "Full Two Fluid Dispersion Solution"
    }
   },
   "outputs": [],
   "source": [
    "fs = 14  # default font size\n",
    "figwidth, figheight = plt.rcParams[\"figure.figsize\"]\n",
    "figheight = 1.6 * figheight\n",
    "fig = plt.figure(figsize=[figwidth, figheight])\n",
    "\n",
    "# Fast mode\n",
    "plt.plot(\n",
    "    plt_vals[\"fast_mode\"][\"x\"],\n",
    "    plt_vals[\"fast_mode\"][\"y\"],\n",
    "    linewidth=2,\n",
    "    label=\"Fast\",\n",
    ")\n",
    "ax = plt.gca()\n",
    "\n",
    "# adjust axes\n",
    "ax.set_xlabel(r\"$(\\omega / k v_A)^2 \\, \\sin \\theta$\", fontsize=fs)\n",
    "ax.set_ylabel(r\"$(\\omega / k v_A)^2 \\, \\cos \\theta$\", fontsize=fs)\n",
    "ax.set_xlim(0.0, 1.5)\n",
    "ax.set_ylim(0.0, 2.0)\n",
    "for spine in ax.spines.values():\n",
    "    spine.set_linewidth(2)\n",
    "ax.minorticks_on()\n",
    "ax.tick_params(which=\"both\", labelsize=fs, width=2)\n",
    "ax.tick_params(which=\"major\", length=10)\n",
    "ax.tick_params(which=\"minor\", length=5)\n",
    "ax.xaxis.set_major_locator(MultipleLocator(0.5))\n",
    "ax.xaxis.set_minor_locator(MultipleLocator(0.1))\n",
    "ax.yaxis.set_major_locator(MultipleLocator(0.5))\n",
    "ax.yaxis.set_minor_locator(MultipleLocator(0.1))\n",
    "\n",
    "\n",
    "# Alfven mode\n",
    "plt.plot(\n",
    "    plt_vals[\"alfven_mode\"][\"x\"],\n",
    "    plt_vals[\"alfven_mode\"][\"y\"],\n",
    "    linewidth=2,\n",
    "    label=\"Alfvén\",\n",
    ")\n",
    "\n",
    "# Acoustic mode\n",
    "plt.plot(\n",
    "    plt_vals[\"acoustic_mode\"][\"x\"],\n",
    "    plt_vals[\"acoustic_mode\"][\"y\"],\n",
    "    linewidth=2,\n",
    "    label=\"Acoustic\",\n",
    ")\n",
    "\n",
    "# annotations\n",
    "plt.legend(fontsize=fs, loc=\"upper right\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
